package git.soulbgm.utils.okhttp;

import git.soulbgm.utils.LogUtil;
import okhttp3.*;

import java.io.*;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;

/**
 * okHttp的工具类
 *
 * @author SoulBGM
 * @date 2019/7/4 13:45
 */
public class OkHttpUtil {

    private static final Map<String, Object> PARAMS = null;

    private final static LogUtil log = new LogUtil(BaseCallback.class);

    /**
     * 将参数添加到RequestBody中
     *
     * @param params 参数
     * @return 添加好参数的RequestBody
     */
    public static RequestBody getRequestBody(Map<String, Object> params) {
        FormBody.Builder builder = new FormBody.Builder();
        if (params != null) {
            Set<String> keySet = params.keySet();
            for (String key : keySet) {
                Object obj = params.get(key);
                builder.add(key, String.valueOf(obj == null ? "" : obj));
            }
        }
        return builder.build();
    }

    /**
     * 仅限GET请求
     * 参数拼接
     *
     * @param params 参数
     * @return ?key=val&key=val 格式的字符串
     */
    public static String paramsJoin(Map<String, Object> params) {
        StringBuilder pathParams = new StringBuilder();
        if (params != null) {
            pathParams.append("?");
            Set<String> keySet = params.keySet();
            for (String key : keySet) {
                Object obj = params.get(key);
                pathParams.append(key);
                pathParams.append("=");
                pathParams.append(obj == null ? "" : String.valueOf(obj));
                pathParams.append("&");
            }
            pathParams.deleteCharAt(pathParams.length() - 1);
        }
        return pathParams.toString();
    }

    /**
     * 同步获取http访问结果
     *
     * @param call Call对象
     * @return http访问返回的结果
     */
    public static byte[] getByteArrayResult(Call call) {
        try {
            Response response = call.execute();
            ResponseBody body = response.body();
            return body != null ? body.bytes() : new byte[0];
        } catch (IOException e) {
            log.error("请求{}出现异常", call.request().url().toString(), e);
            return new byte[0];
        }
    }

    /**
     * 同步获取http访问结果
     *
     * @param call Call对象
     * @return http访问返回的结果
     */
    public static byte[] getStringResult(Call call) {
        byte[] bytes = getByteArrayResult(call);
        return bytes != null ? bytes : new byte[0];
    }

    /**
     * 非空检验
     *
     * @param object  对象
     * @param message 如果为空报的异常信息
     */
    private static void notNull(Object object, String message) {
        if (message == null) {
            message = "No Object specified";
        }
        if (object == null) {
            throw new IllegalArgumentException(message);
        }
    }

    /**
     * 获得OkHttpClient对象
     * 通过url判断是https请求还是http请求
     *
     * @param url 地址
     * @return OkHttpClient对象
     */
    public static OkHttpClient getOkHttpClient(String url) {
        OkHttpClient.Builder builder = new OkHttpClient.Builder();
        OkHttpClient okHttpClient = null;
        if (url.startsWith("https")) {
            okHttpClient = builder
                    .readTimeout(5, TimeUnit.SECONDS)
                    .writeTimeout(5, TimeUnit.SECONDS)
                    .connectTimeout(5, TimeUnit.SECONDS)
                    .sslSocketFactory(SSLSocketClient.getSSLSocketFactory())
                    .hostnameVerifier(SSLSocketClient.getHostnameVerifier())
                    .build();
        } else {
            okHttpClient = builder.build();
        }
        return okHttpClient;
    }

    /**
     * 获得Call对象
     *
     * @param url    地址
     * @param params 参数
     * @return Call对象
     */
    public static Call getCall(String url, Map<String, Object> params) {
        OkHttpClient okHttpClient = getOkHttpClient(url);
        url = url + paramsJoin(params);
        Request request = new Request.Builder()
                .url(url)
                .get()
                .build();
        return okHttpClient.newCall(request);
    }

    /**
     * 同步get请求
     *
     * @param url    地址
     * @param params 参数
     * @return http访问返回的结果
     */
    public static String get(String url, Map<String, Object> params) {
        Call call = getCall(url, params);
        return new String(getStringResult(call));
    }

    /**
     * 同步get请求
     *
     * @param url    地址
     * @param params 参数
     * @return http访问返回的结果
     */
    public static byte[] getByte(String url, Map<String, Object> params) {
        Call call = getCall(url, params);
        return getByteArrayResult(call);
    }

    /**
     * 同步get请求
     *
     * @param url 地址
     * @return http访问返回的结果
     */
    public static String get(String url) {
        return get(url, PARAMS);
    }

    /**
     * 同步get请求
     *
     * @param url 地址
     * @return http访问返回的结果
     */
    public static byte[] getByte(String url) {
        return getByte(url, PARAMS);
    }

    /**
     * 异步get请求
     *
     * @param url      地址
     * @param params   参数
     * @param callback Callback回调对象
     */
    public static void get(String url, Map<String, Object> params, Callback callback) {
        notNull(callback, "No Callback specified");
        Call call = getCall(url, params);
        call.enqueue(callback);
    }

    /**
     * 异步get请求
     *
     * @param url      地址
     * @param callback Callback回调对象
     */
    public static void get(String url, Callback callback) {
        get(url, PARAMS, callback);
    }

    /**
     * 异步下载文件
     *
     * @param url       地址
     * @param localPath 本地路径
     */
    public static void downloadAsync(String url, String localPath) throws IOException {
        downloadAsync(url, localPath, null);
    }

    /**
     * 异步下载文件
     *
     * @param url       地址
     * @param localPath 本地路径
     * @param latch     倒数计时器CountDownLatch对象
     */
    public static void downloadAsync(String url, String localPath, CountDownLatch latch) throws IOException {
        Call call = getCall(url, null);

        File file = new File(localPath);
        forceMkdirParent(file);
        call.enqueue(new BaseCallback() {

            @Override
            public void onFailure(Call call, IOException e) {
                super.onFailure(call, e);
                if (latch != null) {
                    latch.countDown();
                }
            }

            @Override
            public void onResponse(Call call, Response response) throws IOException {
                download(response, file);
                if (latch != null) {
                    latch.countDown();
                }
            }
        });
    }

    /**
     * 同步下载文件
     *
     * @param url       地址
     * @param localPath 本地路径
     */
    public static boolean downloadSync(String url, String localPath) throws IOException {
        Call call = getCall(url, null);
        File file = new File(localPath);
        forceMkdirParent(file);
        Response response = call.execute();
        return download(response, file);
    }

    /**
     * 下载文件
     *
     * @param response 响应
     * @param file     本地文件
     * @return 下载成功返回true否则false
     */
    private static boolean download(Response response, File file) throws IOException {
        ResponseBody body = response.body();
        if (body != null) {
            try (
                    InputStream is = body.byteStream();
                    FileOutputStream fos = new FileOutputStream(file)
            ) {
                if (is != null) {
                    copy(is, fos);
                    return true;
                }
            }
        }
        return false;
    }

    /**
     * 获得post请求的Call对象
     *
     * @param url    地址
     * @param params 参数
     * @return Call对象
     */
    public static Call getPostCall(String url, Map<String, Object> params) {
        OkHttpClient okHttpClient = getOkHttpClient(url);
        RequestBody requestBody = getRequestBody(params);
        Request request = new Request.Builder()
                .url(url)
                .post(requestBody)
                .build();
        return okHttpClient.newCall(request);
    }

    /**
     * 同步post请求
     *
     * @param url    地址
     * @param params 参数
     * @return http访问返回的结果
     */
    public static String post(String url, Map<String, Object> params) {
        return new String(postByte(url, params));
    }

    /**
     * 同步post请求
     *
     * @param url    地址
     * @param params 参数
     * @return http访问返回的结果
     */
    public static byte[] postByte(String url, Map<String, Object> params) {
        OkHttpClient okHttpClient = getOkHttpClient(url);
        RequestBody requestBody = getRequestBody(params);
        Request request = new Request.Builder()
                .url(url)
                .post(requestBody)
                .build();
        Call call = okHttpClient.newCall(request);
        return getStringResult(call);
    }

    /**
     * 同步post请求
     *
     * @param url 地址
     * @return http访问返回的结果
     */
    public static String post(String url) {
        return post(url, PARAMS);
    }

    /**
     * 同步post请求
     *
     * @param url 地址
     * @return http访问返回的结果
     */
    public static byte[] postByte(String url) {
        return postByte(url, PARAMS);
    }

    /**
     * 异步post请求
     *
     * @param url      地址
     * @param params   参数
     * @param callback Callback回调对象
     */
    public static void post(String url, Map<String, Object> params, Callback callback) {
        OkHttpClient okHttpClient = getOkHttpClient(url);
        RequestBody requestBody = getRequestBody(params);
        Request request = new Request.Builder()
                .url(url)
                .post(requestBody)
                .build();
        Call call = okHttpClient.newCall(request);
        call.enqueue(callback);
    }

    /**
     * 异步post请求
     *
     * @param url      地址
     * @param callback Callback回调对象
     */
    public static void post(String url, Callback callback) {
        post(url, PARAMS, callback);
    }

    public static void forceMkdirParent(final File file) throws IOException {
        final File parent = file.getParentFile();
        if (parent == null) {
            return;
        }
        forceMkdir(parent);
    }

    public static void forceMkdir(final File directory) throws IOException {
        if (directory.exists()) {
            if (!directory.isDirectory()) {
                final String message =
                        "File "
                                + directory
                                + " exists and is "
                                + "not a directory. Unable to create directory.";
                throw new IOException(message);
            }
        } else {
            if (!directory.mkdirs()) {
                // Double-check that some other thread or process hasn't made
                // the directory in the background
                if (!directory.isDirectory()) {
                    final String message =
                            "Unable to create directory " + directory;
                    throw new IOException(message);
                }
            }
        }
    }

    public static int copy(final InputStream input, final OutputStream output) throws IOException {
        final long count = copyLarge(input, output);
        if (count > Integer.MAX_VALUE) {
            return -1;
        }
        return (int) count;
    }

    private static final int DEFAULT_BUFFER_SIZE = 1024 * 4;

    public static final int EOF = -1;

    public static long copyLarge(final InputStream input, final OutputStream output)
            throws IOException {

        byte[] buffer = new byte[DEFAULT_BUFFER_SIZE];
        long count = 0;
        int n = 0;
        while (EOF != (n = input.read(buffer))) {
            output.write(buffer, 0, n);
            count += n;
        }
        return count;
    }

}